package com.example.shiro_demo1.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * shiro自定义配置器，在该类中可以规定请求拦截方法，规定Realm等
 * @author cxp
 * @version 1.0
 * @date 2019/6/26 18:15
 */
@Configuration
public class ShiroConfiguration {

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")SecurityManager manager){
        //shiro的过滤器bean对象
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);
        //当前为绝对路径，如果设置为相对路径时下面的/user/**验证不通过时就会跳转到/user/unauthorized，很显然会
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/index");
        bean.setUnauthorizedUrl("/unauthorized");
        //定义一个
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        //anon表示添加可以跳过登录认证的uri
        filterChainDefinitionMap.put("/login","anon");
        filterChainDefinitionMap.put("/loginUser","anon");
        filterChainDefinitionMap.put("/v2/api-docs","anon");
        filterChainDefinitionMap.put("/webjars/**","anon");
        filterChainDefinitionMap.put("/swagger-resources/**","anon");
        filterChainDefinitionMap.put("/swagger-ui.html","anon");
        filterChainDefinitionMap.put("/docs.html","anon");
        filterChainDefinitionMap.put("/doc.html","anon");
        //下面的过滤表示该映射必须要当前用户具备该角色或者该权限才可以访问
        filterChainDefinitionMap.put("/admin","roles[admin]");
        filterChainDefinitionMap.put("/edit","perms[edit]");
        //**表示任何请求
        filterChainDefinitionMap.put("/user/**","perms[user]");
        //authc表示添加需要登录认证才能访问的uri,一般情况下都是将除了登录等请求url都放进去
        filterChainDefinitionMap.put("/**","authc");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return bean;
    }

    /**
     * 注入一个securityManager
     * 原本以前我们是可以通过ini配置文件完成的，代码如下：
     * 1、获取SecurityManager工厂，此处使用Ini配置文件初始化SecurityManager
     * Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
     * 2、得到SecurityManager实例 并绑定给SecurityUtils
     * SecurityManager securityManager = factory.getInstance();
     * SecurityUtils.setSecurityManager(securityManager);
     * @param shiroRealm
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
        //这个DefaultWebSecurityManager构造函数,会对Subject，realm等进行基本的参数注入
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        //往SecurityManager中注入Realm，代替原本的默认配置
        manager.setRealm(shiroRealm);
        return manager;
    }

    /**
     * 自定义的Realm
     * @param matcher
     * @return
     */
    @Bean("shiroRealm")
    public ShiroRealm authRealm(@Qualifier("hashedCredentialsMatcher") RetryLimitHashedCredentialsMatcher matcher) {
        ShiroRealm authRealm = new ShiroRealm();
        //这边可以选择是否将认证的缓存到内存中，现在有了这句代码就将认证信息缓存的内存中了
        authRealm.setCacheManager(new MemoryConstrainedCacheManager());
        //最简单的情况就是明文直接匹配，然后就是加密匹配，这里的匹配工作则就是交给CredentialsMatcher来完成
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }

    /**
     * Realm在验证用户身份的时候，要进行密码匹配
     * 最简单的情况就是明文直接匹配，然后就是加密匹配，这里的匹配工作则就是交给CredentialsMatcher来完成
     * 支持任意数量的方案，包括纯文本比较、散列比较和其他方法。除非该方法重写，否则默认值为
     * @return
     */
    @Bean("hashedCredentialsMatcher")
    public RetryLimitHashedCredentialsMatcher hashedCredentialsMatcher() {
        RetryLimitHashedCredentialsMatcher credentialMatcher = new RetryLimitHashedCredentialsMatcher();
        // 采用MD5方式加密
        credentialMatcher.setHashAlgorithmName("MD5");
        // 设置加密次数
        credentialMatcher.setHashIterations(1024);
        return credentialMatcher;
    }

    /**
     * 以下AuthorizationAttributeSourceAdvisor,DefaultAdvisorAutoProxyCreator两个类是为了支持shiro注解
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

}
